home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-05 / pcrte224.zip / SOURCE.ZIP / WD8003.INC < prev    next >
Text File  |  1992-06-09  |  20KB  |  510 lines

  1. ;;******************************************************************************
  2. ;;                         wd8003.inc      wd8003.inc
  3. ;;******************************************************************************
  4. ;;
  5. ;;  Copyright (C) 1989 Northwestern University, Vance Morrison
  6. ;;
  7. ;;
  8. ;; Permission to view, compile, and modify for LOCAL (intra-organization) 
  9. ;; USE ONLY is hereby granted, provided that this copyright and permission 
  10. ;; notice appear on all copies.  Any other use by permission only.
  11. ;;
  12. ;; Northwestern University makes no representations about the suitability 
  13. ;; of this software for any purpose.  It is provided "as is" without expressed 
  14. ;; or implied warranty.  See the copywrite notice file for complete details.
  15. ;;
  16. ;;******************************************************************************
  17. ;; wd8003 holds the interface routines for the western digital ethernet card 
  18. ;; WD8003E or the starlan card WD8003S.
  19. ;;
  20. ;; The functions provided by this file are
  21. ;;
  22. ;;   WD_DECLARE name, io_address, shr_seg, shr_off    
  23. ;;   WD_DEFINE name
  24. ;;   WD_IF_R_ACCESS_out_BX_CX_ES name, no_packet
  25. ;;   WD_IF_R_CONT_in_BX_CX_ES_const_BX_CX_DX_BP_SI_DI_ES name, ok
  26. ;;   WD_IF_R_FREE_const_BX_CX_BP_SI_DI_ES name
  27. ;;   WD_IF_W_ACCESS_in_CX_out_DI_ES_const_BX_CX_BP name, no_buffer
  28. ;;   WD_IF_W_WRITE_in_CX_const_BX_BP_ES name
  29. ;;   WD_IF_SET_ADDRESS_in_SI_const_BX_CX_BP_DI_ES name
  30. ;;   WD_IF_COPY_in_CX_SI_DI_ES_out_SI_DI_const_BX_BP_ES name
  31. ;;
  32. ;; Variables set by this module
  33. ;;
  34. ;;   wd_&name&_declared                     ;; one if this interface exists
  35. ;;   if_&name&_address                      ;; the hardware address
  36. ;;   if_&name&_mtu                          ;; the max trans unit
  37. ;;
  38. ;;******************************************************************************
  39.  
  40.     include wd.inc
  41.  
  42. ;;******************************************************************************
  43. ;; data storage needed by this module
  44.  
  45. wd_q_entry  STRUC
  46.     wd_q_start      DW ?
  47.     wd_q_end        DW ?
  48.     wd_q_len        DW ?
  49. wd_q_entry  ENDS
  50.  
  51. wd_data  STRUC
  52.    wd_new_bndry      DB 0
  53.    wd_dequeue        DB 0           ;; please dequeue the last packet
  54. wd_data ENDS
  55.  
  56.  
  57. ;;******************************************************************************
  58. ;;   IF_DECLARE name, io_address, shr_seg, shr_off, wbuffs
  59. ;;       declares an interface object.  'io_address' is the address of the
  60. ;;       start of the 8003E control registers.  'shr_seg'  and
  61. ;;       'shr_off' is the address of the WD8003 card buffer
  62. ;;       This address must be a multiple of 512.  'timer' is a timer
  63. ;;      object. 'wbuffs' is the number of 256byte blocks to allocate to
  64. ;;      writing (should be 6 for ethernet, more like 16 for starlan)
  65. ;;
  66. WD_DECLARE MACRO name, io_address, shr_seg, shr_off, timer, wbuffs
  67.     .errb <name>
  68.     .errb <io_address>
  69.     .errb <shr_seg>
  70.     .errb <shr_off>
  71.  
  72.     .DATA
  73.     wd_&name&_declared  = 1
  74.     wd_&name&_io           = io_address          ;; set compile time values
  75.     wd_&name&_shared_off   = shr_off
  76.     wd_&name&_shared_seg   = shr_seg
  77.  
  78.     wd_&name&_timer = timer
  79.     wd_&name&_buff = name*100+1     ;; make up names in my space
  80.     wd_&name&_wqueue = name*100+2
  81.     ifb <wbuffs>
  82.         wd_&name&_wbuffs = 6
  83.     else 
  84.         wd_&name&_wbuffs = wbuffs 
  85.     endif
  86.  
  87.     if_&name&_mtu = 1514
  88.     global wd_&name&_data:wd_data
  89.     global if_&name&_address:word 
  90.  
  91.     .CODE 
  92.     global wd_&name&_dequeue:near
  93.     global wd_&name&_real_define:near
  94.  
  95.     BUFF_DECLARE %wd_&name&_buff, %(wbuffs*256), shr_off
  96.     QUEUE_DECLARE %wd_&name&_wqueue, wbuffs, %(size wd_q_entry)
  97. ENDM
  98.  
  99.  
  100. ;;******************************************************************************
  101. ;;   IF_DEFINE name
  102. ;;      sets asside memory an interface object and initializes it.  This
  103. ;;      routine is a no-op if 'name' was not declared
  104. ;;
  105. WD_DEFINE MACRO name
  106.  
  107.     call wd_&name&_real_define
  108. ENDM
  109.  
  110. WD_REAL_DEFINE MACRO name
  111.     LOCAL loop1, loop2, loop3, around
  112.     .errb <name>
  113.  
  114. ifdef wd_&name&_declared
  115.     .DATA
  116.     if_&name&_address DW 3 DUP (0)
  117.     wd_&name&_data wd_data      <>  ;; create storage needed
  118.  
  119.     .CODE
  120.     jmp around
  121.         wd_&name&_dequeue:
  122.             WD_IF_W_DEQUEUE_const_BX_BP_ES name         
  123.             TIMER_RETURN %wd_&name&_timer
  124.     around:
  125.     wd_&name&_real_define:
  126.     BUFF_DEFINE %wd_&name&_buff         ;; initialize the buff manager
  127.  
  128.     QUEUE_DEFINE %wd_&name&_wqueue      ;; and write queue
  129.  
  130.     mov cx, 6                    ;; get the ethernet address
  131.     mov bx, OFFSET if_&name&_address
  132.     mov dx, wd_&name&_io+ADDROM  ;; point to the begining of the io space
  133.     loop1:                       ;; which is the reg holding the Eth addr
  134.         in AL, DX
  135.         mov [BX], AL        
  136.         inc DX
  137.         inc BX
  138.     loop loop1
  139.  
  140.     mov AL, 80h                  ;; reset the card
  141.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES 0, wd_&name&_io   
  142.     mov AL, 00h
  143.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES 0, wd_&name&_io
  144.  
  145.            ;; register 0 is the MSR (Memory base reg)
  146.     mov AL, (wd_&name&_shared_seg+(wd_&name&_shared_off/16))/512 
  147.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES 0, wd_&name&_io
  148.  
  149.     mov AL, MSK_PG0 + MSK_RD2               ;; make sure we are on page 0
  150.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
  151.     mov AL, MSK_BMS + MSK_FT10              ;; select FIFO threshold = 8 bytes
  152.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES DCR, wd_&name&_io
  153.     xor AL, AL                              ;; clear RBCR0,1
  154.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES RBCR0, wd_&name&_io
  155.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES RBCR1, wd_&name&_io
  156.     mov AL, MSK_MON                         ;; turn off receiving
  157.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES RCVR, wd_&name&_io
  158.     xor AL, AL                              ;; clear TCR
  159.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES TCR, wd_&name&_io
  160.  
  161.     mov AL, STOP_PG                    ;; end of input buffer (in 256b pages)
  162.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES PSTOP, wd_&name&_io
  163.     mov AL, wd_&name&_wbuffs ;; start of input buffer (in 256b pages)
  164.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES PSTART, wd_&name&_io  
  165.         ;; Boundry of read in from not read in yet
  166.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES BNRY, wd_&name&_io    
  167.     mov AL, -1                              
  168.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES ISR, wd_&name&_io 
  169.     mov AL, 0                               ;; no interupts
  170.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES IMR, wd_&name&_io 
  171.  
  172.     mov AL, MSK_PG1 + MSK_RD2               ;; make sure we are on page 1
  173.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
  174.     mov AL, wd_&name&_wbuffs+1     ;; Set input pointer for queue 
  175.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CURR, wd_&name&_io
  176.  
  177.     mov CX, 6                               ;; set the ethernet address
  178.     mov BX, OFFSET if_&name&_address
  179.     mov DX, wd_&name&_io+PAR0
  180.     loop2:
  181.         mov AL, [BX]        ;; get 1 byte into AL
  182.         out DX, AL          ;; write to PAR
  183.         inc BX
  184.         inc DX
  185.     loop loop2
  186.  
  187.     mov CX, 8                           ;; set the multicast address to all 0's
  188.     mov DX, wd_&name&_io+MAR0
  189.     xor AL, AL          
  190.     loop3:
  191.         out DX, AL
  192.         inc DX
  193.     loop loop3                  
  194.  
  195.     mov AL, MSK_PG0 + MSK_RD2                   ;; make sure we are on page 0
  196.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
  197.     mov AL, MSK_STA + MSK_RD2                   ;; activate the 8390
  198.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io    
  199.  
  200.     mov AL, MSK_AB                              ;; turn on receiver
  201.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES RCVR, wd_&name&_io 
  202.  
  203.     mov byte ptr wd_&name&_data.wd_dequeue, 0   ;; no packet to dequeue
  204.     RET
  205. endif
  206. ENDM
  207.  
  208.  
  209. ;;******************************************************************************
  210. ;;   IF_R_ACCESS_out_BX_ES name, no_packet
  211. ;;       IF_R_ACCESS waits for the next packet to come from the the board
  212. ;;       associated with 'name' and returns a pointer to the begining of 
  213. ;;       an ethernet packet in BX:ES.  CX holds the length of the packet
  214. ;;       R_ACCESS jumps to 'no_packet' if there are no packets waiting to 
  215. ;;       be read in
  216. ;;       
  217. WD_IF_R_ACCESS_out_BX_CX_ES MACRO name, no_packet
  218.     local inside, good_packet, wrapped, new_wrapped, bad_packet, ok_status
  219.     local good_length, truncate
  220.     .errb <no_packet>
  221.  
  222.     mov AL, MSK_PG0 + MSK_RD2              ;; read the BNRY register into AL
  223.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
  224.     READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES BNRY, wd_&name&_io
  225.  
  226.     inc AL                                  ;; increment with wrap around
  227.     cmp AL, STOP_PG
  228.     jb inside
  229.         mov AL, wd_&name&_wbuffs
  230.     inside:
  231.     mov BH, AL                              ;; save it in BH
  232.  
  233.     mov AL, MSK_PG1 + MSK_RD2               ;; read CURR register into AL
  234.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
  235.     READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES CURR, wd_&name&_io
  236.  
  237.     cmp AL, BH
  238.     je no_packet
  239.     xor BL, BL                                 ;; BX now holds pointer to packet
  240.  
  241.     mov DX, wd_&name&_shared_seg          ;; ES = segment address
  242.     mov ES, DX
  243.     mov AH, ES:[BX+wd_&name&_shared_off]   ;; get the status
  244.     cmp AH, SMK_PRX                              ;; is it good
  245.     jz ok_status
  246.         cmp AH, SMK_PRX+SMK_PHY
  247.     jnz bad_packet
  248.  
  249.     ok_status:
  250.     mov AH, ES:[BX+1+wd_&name&_shared_off] ;; pointer to the next packet
  251.         ;; sanity check on next packet pointer AH
  252.     cmp BH, AL                              ;; is BNDRY+1 <= CURR?
  253.     ja wrapped
  254.         cmp AH, BH
  255.         jb bad_packet
  256.         cmp AH, AL
  257.         jbe good_packet
  258.         jmp bad_packet
  259.     wrapped:
  260.         cmp AH, BH
  261.         jb new_wrapped
  262.             cmp AH, STOP_PG
  263.             jnb bad_packet
  264.             jmp good_packet
  265.         new_wrapped:
  266.             cmp AH, wd_&name&_wbuffs
  267.             jb bad_packet
  268.             cmp AH, AL
  269.             jbe good_packet
  270.     bad_packet:
  271.         ;; set BNDRY = BNDRY+1 and try again
  272.         mov AL, MSK_PG0 + MSK_RD2             ;; make sure we are on page 0
  273.         WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
  274.  
  275.         mov AL, BH                            ;; write the new BNRY register
  276.         WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES BNRY, wd_&name&_io    
  277.         jmp no_packet                         ;; return bad status
  278.     good_packet:
  279.  
  280.     mov wd_&name&_data.wd_new_bndry, AH        ;; save it for R_FREE
  281.     mov CX, ES:[BX+wd_&name&_shared_off+2]     ;; load the length
  282.     add BX, wd_&name&_shared_off+4 ;; BX point to begining of the packet
  283.     sub CX, 4
  284.  
  285.     cmp CX, 1514                               ;; sanity check
  286.     jle good_length
  287.         cmp CH, CL
  288.         jne truncate
  289.             xor CH, CH                          ;; fix western digital bug
  290.             jmp good_length
  291.         truncate:
  292.         mov CX, 1514
  293.     good_length:
  294. ENDM
  295.  
  296.  
  297. ;;******************************************************************************
  298. ;;   IF_R_FREE_const_BX_CX_BP_SI_DI_ES  name
  299. ;;       After the client is through processing the packet returned by 
  300. ;;       IF_R_ACCESS, IF_R_FREE must be called to inform 'name' that the 
  301. ;;       memory that the packet was in can be reused for future packets.
  302. ;;
  303. WD_IF_R_FREE_const_BX_CX_BP_SI_DI_ES MACRO name
  304.     local inside
  305.     .errb <name>
  306.  
  307.     mov AL, MSK_PG0 + MSK_RD2                    ;; make sure we are on page 0
  308.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
  309.  
  310.     mov AL, wd_&name&_data.wd_new_bndry       ;; Retreive NEW_BOUNDRY
  311.     dec AL 
  312.     cmp AL, wd_&name&_wbuffs
  313.     jge inside
  314.         mov AL, STOP_PG-1
  315.     inside:                         ;; write the new BNRY register
  316.     WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES BNRY, wd_&name&_io    
  317. ENDM
  318.  
  319.  
  320. ;;******************************************************************************
  321. ;;   WD_IF_R_CONT_in_BX_CX_ES name, ok
  322. ;;       IF_R_CONT determines if the packet returned by R_READ in BX:ES
  323. ;;       of length CX is continuous.  If it is it jumps to 'ok' otherwise
  324. ;;       it just returns
  325. ;;
  326. WD_IF_R_CONT_in_BX_CX_ES_const_BX_CX_DX_BP_SI_DI_ES MACRO name, ok
  327.     .errb <ok>
  328.  
  329.     mov AX, BX
  330.     add AX, CX
  331.     cmp AX, OFFSET wd_&name&_shared_off+STOP_PG*256
  332.     jb ok
  333. ENDM
  334.  
  335.  
  336. ;;******************************************************************************
  337. ;;   IF_W_ACCESS_in_CX_out_DI_ES name, no_buffer
  338. ;;       IF_W_ACCESS returns a pointer to an output buffer for a packet.  The 
  339. ;;       pointer is returned in DI:ES.  If the ouptut buffer is busy, this 
  340. ;;       routine will jump to 'no_buffer'.  The output buffer  min(CX, 1536) 
  341. ;;       bytes long
  342. ;;
  343. WD_IF_W_ACCESS_in_CX_out_DI_ES_const_BX_CX_BP MACRO name, no_buffer
  344.     .errb <no_buffer>
  345.  
  346.     BUFF_CHECK_in_CX_out_SI_DI_const_BX_CX_DX_BP_ES %wd_&name&_buff, no_buffer
  347.     mov DI, SI
  348.     mov SI, offset wd_&name&_shared_seg
  349.     mov ES, SI
  350. ENDM
  351.  
  352.  
  353. ;;******************************************************************************
  354. ;;   IF_W_WRITE_in_CX name
  355. ;;       IF_W_WRITE actually signals the ethernet board to write a packet to 
  356. ;;       the ethernet.  The packet is assumed to be in the buffer returned by 
  357. ;;       IF_W_ACCESS. CX is the length of the packet to send.  
  358. ;;
  359. WD_IF_W_WRITE_in_CX_const_BX_BP_ES MACRO name
  360.     local done
  361.     .errb <name>
  362.  
  363.         ;; queue up the packet
  364.     mov DX, CX          ;; save CX
  365.     add CX, 255         ;; round up to a multiple of 256
  366.     xor CL, CL
  367.     BUFF_CHECK_in_CX_out_SI_DI_const_BX_CX_DX_BP_ES %wd_&name&_buff, done
  368.     mov CX, DI          ;; save buff_end
  369.     QUEUE_ENQUEUE_out_DI_const_BX_CX_DX_BP_SI_ES %wd_&name&_wqueue, done
  370.     xchg CX, DI         ;; restore DI, save queue entry
  371.     BUFF_GET_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %wd_&name&_buff
  372.     xchg DI, CX         ;; restore queue entry, save_end
  373.     mov [DI+wd_q_start], SI
  374.     mov [DI+wd_q_end], CX
  375.     mov [DI+wd_q_len], DX       
  376.     WD_IF_W_DEQUEUE_const_BX_BP_ES name, 1
  377.     done:
  378. ENDM
  379.  
  380. ;;******************************************************************************
  381. ;; WD_IF_W_DEQUEUE tryes to empty the write queue.  It checks if there is
  382. ;; something in the queue, and if there is, it tries to send it.  If the
  383. ;; queue is not empty when it leaves, it submits itself to go off again so
  384. ;; that it can try again.  'write' is true (not_blank) if this is called
  385. ;; from W_WRITE
  386. ;;
  387. WD_IF_W_DEQUEUE_const_BX_BP_ES MACRO name, write
  388.     local done, requeue, not_dequeueing, no_more
  389.     .errb <name>
  390.  
  391.     mov CL, wd_&name&_data.wd_dequeue           ;; save the old value
  392.     READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
  393.     test AL, MSK_TXP
  394.     ifnb <write>
  395.       jnz done
  396.     else
  397.       jnz requeue
  398.     endif
  399.         cmp CL, 0    ;; dequeue the next packet ?
  400.         jz not_dequeueing
  401.             QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES %wd_&name&_wqueue,no_more 
  402.             mov DI, word ptr [SI+wd_q_end]
  403.             BUFF_FREE_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %wd_&name&_buff
  404.             QUEUE_DEQUEUE_in_SI_const_AX_BX_CX_DX_BP_DI_ES %wd_&name&_wqueue
  405.         not_dequeueing:
  406.  
  407.             ;; is there another to send?
  408.         QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES %wd_&name&_wqueue, no_more
  409.  
  410.             ;; send the packet
  411.         mov AL, MSK_PG0 + MSK_RD2        ;; make sure we are in register page 0
  412.         WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
  413.         mov AX, [SI+wd_q_len]
  414.         WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES TBCR0, wd_&name&_io
  415.         mov AL, AH
  416.         WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES TBCR1, wd_&name&_io
  417.  
  418.         mov AL, byte ptr [SI+wd_q_start+1]  ;; where the packet starts
  419.         WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES TPSR, wd_&name&_io
  420.         mov AL, MSK_TXP + MSK_RD2         ;; send the packet
  421.         WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
  422.         mov wd_&name&_data.wd_dequeue, 1
  423.  
  424.         ifnb <write>
  425.             or CL, CL
  426.             jnz done
  427.         endif
  428.     requeue:
  429.             ;; submit it to the timer queue
  430.         xor AX, AX
  431.         mov CX, BX              ;; save BX
  432.         TIMER_MARK_in_AX_const_CX_BP_ES %wd_&name&_timer, wd_&name&_dequeue
  433.         mov BX, CX              ;; restore BX
  434.         jmp done
  435.     no_more:
  436.         mov wd_&name&_data.wd_dequeue, 0
  437.     done:
  438. ENDM
  439.  
  440.  
  441. ;;******************************************************************************
  442. ;;   IF_SET_ADDRESS_in_SI name
  443. ;;       IF_SET_ADDRESS_in_SI sets the hardware address to be the value
  444. ;;       pointed to by SI.  Note this function may be a no-op if the
  445. ;;       hardware address cannot be set (ETHERNET for example)
  446. ;;
  447.  
  448. WD_IF_SET_ADDRESS_in_SI_const_BX_CX_BP_DI_ES MACRO name
  449.     .err    ;; we don't support setting ethernet addresses (yet)
  450.     ENDM
  451.  
  452.  
  453. ;;******************************************************************************
  454. ;;   IF_COPY_in_CX_SI_DI_ES_out_SI_DI name
  455. ;;      IF_COPY_in_CX_SI_DI_ES copys a packet from the input buffer (pointed 
  456. ;;      to by SI and the segement register given in IF_DECLARE) to an output 
  457. ;;      buffer (pointed to by DI and dest_reg) of length CX.   It assumes the
  458. ;;      output buffer is contiguous.  (and the caller shouldn't care if the 
  459. ;;      input buffer is contiguous)  COPY updates the pointers SI and DI
  460. ;;      to the end of the packet, and COPY could be called again if CX is not
  461. ;;      the total packet length (Note that CX MUST be even if you care about
  462. ;;      SI, and DI being updated properly)
  463. ;;
  464. WD_IF_COPY_in_CX_SI_DI_ES_out_SI_DI_const_BX_BP_ES MACRO name
  465.     local wrap, done
  466.     .errb <name>
  467.  
  468.     mov DX, DS                           ;; save DS
  469.     mov AX, wd_&name&_shared_seg  
  470.     mov DS, AX
  471.  
  472.     mov AX, OFFSET wd_&name&_shared_off+STOP_PG*256
  473.     sub AX, SI                            ;; AX holds length to wrap line
  474.     CMP  AX, CX
  475.     jl wrap                               ;; wrap if AX less than packet lenght
  476.         inc CX                            ;; AX >= CX, no wrap is necessary
  477.         shr CX,1
  478.         rep movsw
  479.         jmp done
  480.     wrap:
  481.         XCHG AX, CX                       ;; length is now length to wrap line
  482.         SUB AX, CX                        ;; AX holds remainder
  483.         inc CX
  484.         shr CX,1
  485.         rep movsw
  486.         mov SI, OFFSET wd_&name&_shared_off+wd_&name&_wbuffs*256
  487.         mov CX, AX
  488.         inc CX
  489.         shr CX,1
  490.         rep movsw
  491.     done:
  492.     mov DS, DX                            ;; restore DS
  493. ENDM
  494.  
  495.  
  496. ;;******************************************************************************
  497. ;; utility functions needed only within this module
  498.  
  499. READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES MACRO port, if_io
  500.     mov DX, if_io+port
  501.     in  AL, DX                              ;; AL contains data read from port
  502. ENDM
  503.  
  504. ;;******************************************************************************
  505. WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES MACRO port, if_io
  506.     mov DX, if_io+port
  507.     out DX, AL                              ;; AL contains data read from port
  508. ENDM 
  509.  
  510.